
#import "Utilities.h"
#import "TreeView.h"

#define LEFTARROWKEY	172
#define RIGHTARROWKEY	174
#define UPARROWKEY		173
#define DOWNARROWKEY	175
#define PUSHKEY			0x3
#define BACKSPACEKEY	0x7f

@interface TreeView(PrivateMethods)

- updateCanvasPos:(NXCoord)xPos :(NXCoord)yPos;

@end
 
@implementation TreeView

- initFrame:(const NXRect *)frameRect
{
	[super initFrame:frameRect];
	xTimes = 1;
	yTimes = 1;
	canvasRect = *frameRect;
	canvasPath = nil;
	[self updateCanvasPos:0 :0];
	[[self setClipping:NO] setFlipped:YES];
	linkPath = nil;
	hitPath = nil;
	agent = nil;
	nodeList = [[List allocFromZone:[self zone]] init];
	pbConversion = NO;
	pbTree = nil;
	return self;
}

- free
{
	if(linkPath)
		[linkPath free];
	if(hitPath)
		[hitPath free];
	[nodeList free];
	return [super free];
}

- (BOOL)lockFocus
{
   	BOOL aBOOL;
   	float rot,gruen,blau;
   
   	if(NXDrawingStatus == NX_DRAWING){
		aBOOL = [super lockFocus];
		PScurrentlinewidth(&drawState.linewidth);
		PScurrentrgbcolor(&rot,&gruen,&blau);
		drawState.color = NXConvertRGBToColor(rot,gruen,blau);
		drawState.font = nil;
		return aBOOL;
   	} else
		return [super lockFocus];
}

- drawSelf:(const NXRect *)r :(int)count
{  	
   	if(count == 1){ 
     	[self drawYourSelf:r];
   	} else {
      	[self drawYourSelf:&r[1]];
      	[self drawYourSelf:&r[2]];
   	}    	         	         	     
   	return self;			
}

- drawYourSelf:(const NXRect *)r
{  
	id tree;
	int i,j;

   	PSsetgray(NX_WHITE);
	NXRectFill(r);
	drawState.color = NX_COLORWHITE;
	if(NXDrawingStatus == NX_DRAWING){
		PSrectviewclip(r->origin.x,r->origin.y,r->size.width,r->size.height);       
		PSsetgray(NX_LTGRAY);
		PSsetlinewidth(0.15);
		drawState.color = NX_COLORLTGRAY;
		drawState.linewidth = 0.15;
		for(i = 0;i <= xTimes;i++)
			for(j = 0;j <= yTimes;j++){
				[self updateCanvasPos:i * NX_WIDTH(&canvasRect) :j * NX_HEIGHT(&canvasRect)];
				[canvasPath send:dps_ustroke cached:YES];
			}
	}
	if(!pbConversion)
		tree = [agent tree];
	else
		tree = pbTree;
	if(tree)
		[self drawTree:tree inRect:r];
	if(NXDrawingStatus == NX_DRAWING)
          PSinitviewclip();	         	         	     
   	return self;			
}

- drawTree:tree inRect:(const NXRect *)r
{
	int i;
	BOOL knobs, attachments;

	[tree layout];		
	if(!linkPath)
		linkPath = [[UPath allocFromZone:[self zone]] init];
	[linkPath resetFill]; 
	[nodeList empty];
	if(NXDrawingStatus == NX_PRINTING)
		[tree involved:NULL addTo:nodeList link:linkPath];
	else
		[tree involved:r addTo:nodeList link:linkPath];
	for(i = 0; i < [nodeList count];i++)
		[[nodeList objectAt:i] drawNodeShadow:&drawState];
	if([tree child] && ![tree zipped]){
		PSsetgray(NX_BLACK);
		drawState.color = NX_COLORBLACK;
		PSsetlinewidth(0.15);
		drawState.linewidth = 0.15;
		[linkPath send:dps_ustroke cached:NO];
	}
	for(i = 0; i < [nodeList count];i++)
		[[nodeList objectAt:i] drawNodeEnding:&drawState];
	for(i = 0; i < [nodeList count];i++)
		[[nodeList objectAt:i] drawNodeFill:&drawState];
	if(NXDrawingStatus == NX_DRAWING){
		knobs = YES;
		attachments = [agent showMarker];
	} else
		knobs = attachments = NO;
	for(i = 0; i < [nodeList count];i++)
		[[nodeList objectAt:i] drawNodeOutline:&drawState knobs:knobs];
	for(i = 0; i < [nodeList count];i++)
		[[nodeList objectAt:i] drawNodeCellsInView:self attachments:attachments];
	return self;	
}	

- mouseDown:(NXEvent *)event
{
    NXPoint p;
	NXRect redrawRect, textFrame;
	NXSize maxTextSize;
	id selectedNode,lastSelected,tree;
	id retval, fe = nil;
	BOOL hitAttach, fieldResponder = NO;

    p = event->location; 
    [self convertPoint:&p fromView:nil];
	[window disableFlushWindow];
    [self  lockFocus];
		tree = [agent tree];
		if(tree){
			if(!hitPath)
				hitPath = [[HitPath allocFromZone:[self zone]] init];
			[hitPath movePathToPoint:&p];
			selectedNode = [tree hit:hitPath];
			if(selectedNode){
				lastSelected = [agent declareSelection:selectedNode];
				if(selectedNode != lastSelected){
					[selectedNode getBounds:&redrawRect];
					[self drawSelf:&redrawRect :1];
					[self scrollRectToVisible:&redrawRect];
					if(lastSelected){
						[lastSelected setSelected:YES];
						[lastSelected getBounds:&redrawRect];
						[lastSelected setSelected:NO];
						[self drawSelf:&redrawRect :1];
					}
				}
				if(event->data.mouse.click >= 2){
					[window endEditingFor:self];
					fe = [window getFieldEditor:YES for:self];
					[fe setFont:[selectedNode font]];
					[fe setCharFilter:NXFieldFilter];
					[fe setFontPanelEnabled:NO];
					[fe setMonoFont:YES];
					[fe setOpaque:YES];
					[selectedNode getTextFrame:&textFrame];
					[fe setMinSize:&(textFrame.size)];
					maxTextSize.width = bounds.size.width;
					maxTextSize.height = textFrame.size.height;
					[fe setMaxSize:&maxTextSize];
					[fe setFrame:&textFrame];
					[fe setHorizResizable:YES];
	    			[fe setText:[selectedNode label]];
					[fe selectAll:self];
	    			[fe setAlignment:NX_CENTERED];
	    			[fe unscript:self];
					[fe setVertResizable:NO];
					[fe setDelegate:selectedNode];
					[self addSubview:fe];
					p.x = textFrame.origin.x;
					p.y = textFrame.origin.y;
					[self convertPoint:&p toView:nil];
					event->location = p;
					[fe mouseDown:event];
					fieldResponder = YES;
				}
			} else if([agent showMarker]){
				selectedNode = [tree hitAttachment:&p :&hitAttach];			
				if(selectedNode){
					[window reenableFlushWindow];
					if(hitAttach)
						retval = [selectedNode activateAttachment:event inView:self];
					else
						retval = [selectedNode activateSound:event inView:self];
					[window disableFlushWindow];
					if(retval){
						lastSelected = [agent declareSelection:selectedNode];
						if(selectedNode != lastSelected){
							[selectedNode getBounds:&redrawRect];
							[self drawSelf:&redrawRect :1];
							if(lastSelected){
								[lastSelected setSelected:YES];
								[lastSelected getBounds:&redrawRect];
								[lastSelected setSelected:NO];
								[self drawSelf:&redrawRect :1];
							}
						}
					}
				} else {
					lastSelected = [agent declareSelection:nil];
					if(lastSelected){
						[lastSelected setSelected:YES];
						[lastSelected getBounds:&redrawRect];
						[lastSelected setSelected:NO];
						[self drawSelf:&redrawRect :1];
					}
				}
			}
		}
    [self  unlockFocus];
	[window reenableFlushWindow];
	[window  flushWindow];
	if(fieldResponder)
		[window makeFirstResponder:fe];
	else
		[window makeFirstResponder:self];
    return self;
}

- keyDown:(NXEvent *)event
{
	id selectedNode = nil,lastSelected,tree,fe;
    NXEvent *eptr = event;
	NXRect redrawRect, textFrame;
	NXSize maxTextSize;

	if(eptr->data.key.charSet == NX_SYMBOLSET){
		lastSelected = [agent currentNode];
		if(!lastSelected)
			return self;
		[window disableFlushWindow];
    	[self  lockFocus];
	    switch(eptr->data.key.charCode){
			case LEFTARROWKEY:
				selectedNode = [lastSelected parent];
		    break;
			case RIGHTARROWKEY:
				if(![lastSelected zipped])
		    		selectedNode = [lastSelected child];
		    break;
			case UPARROWKEY:
		    	tree = [lastSelected parent];
				if(tree){
					tree = [tree child];
					if(tree == lastSelected)
						selectedNode = nil;
					else {
						while([tree sibling] != lastSelected)
							tree = [tree sibling];
						selectedNode = tree;
					}
				} else
					selectedNode = nil;
		    break;
			case DOWNARROWKEY:
		    	selectedNode = [lastSelected sibling];
		    break;
			default:
		    break;
	    }
		if(selectedNode){
			[agent declareSelection:selectedNode];
			[selectedNode getBounds:&redrawRect];
			[self drawSelf:&redrawRect :1];
			[self scrollRectToVisible:&redrawRect];
			[lastSelected setSelected:YES];
			[lastSelected getBounds:&redrawRect];
			[lastSelected setSelected:NO];
			[self drawSelf:&redrawRect :1];
		}
		[self  unlockFocus];
		[window reenableFlushWindow];
		[window  flushWindow];
		[window makeFirstResponder:self];
	} else if(eptr->data.key.charSet == NX_ASCIISET && eptr->data.key.charCode == PUSHKEY){
		lastSelected = [agent currentNode];
		if(!lastSelected)
			return self;
		[self  lockFocus];
		[window endEditingFor:self];
		fe = [window getFieldEditor:YES for:self];
		[fe setFont:[lastSelected font]];
		[fe setCharFilter:NXFieldFilter];
		[fe setFontPanelEnabled:NO];
		[fe setMonoFont:YES];
		[fe setOpaque:YES];
		[lastSelected getTextFrame:&textFrame];
		[fe setMinSize:&(textFrame.size)];
		maxTextSize.width = bounds.size.width;
		maxTextSize.height = textFrame.size.height;
		[fe setMaxSize:&maxTextSize];
		[fe setFrame:&textFrame];
		[fe setHorizResizable:YES];
	    [fe setText:[lastSelected label]];
	    [fe setAlignment:NX_CENTERED];
	    [fe unscript:self];
		[fe setVertResizable:NO];
		[fe setDelegate:lastSelected];
		[self addSubview:fe];
		[fe selectAll:self];
		[self  unlockFocus];
		[window  flushWindow];
		[window makeFirstResponder:fe];
	} else if(eptr->data.key.charSet == NX_ASCIISET && eptr->data.key.charCode == BACKSPACEKEY)
		[agent delete:self];
    return self;
}

- (BOOL)checkResize:(const NXRect *)r for:tree
{
	NXRect treeBounds;
	NXCoord dlx, dly, dux, duy;
	BOOL result = NO;
	NXPoint p;
	int tx,ty;

	[tree layout];
	[tree getTreeBounds:&treeBounds lowerWidth:&dlx lowerHeight:&dly];
	dux = NX_WIDTH(&treeBounds) - dlx; 
	duy = NX_HEIGHT(&treeBounds) - dly;
	[self convertRect:&treeBounds toView:superview];
	if(r){
		[self sizeTo:NX_WIDTH(r) :NX_HEIGHT(r)];
		canvasRect = *r;
		xTimes = yTimes = 1;
		result = YES;
	}
	tx = ceil(NX_WIDTH(&treeBounds) / NX_WIDTH(&canvasRect));
	ty = ceil(NX_HEIGHT(&treeBounds) / NX_HEIGHT(&canvasRect));
	if(ty != yTimes || tx != xTimes){
		xTimes = tx;
		yTimes = ty;
		[self sizeTo:NX_WIDTH(&canvasRect) * xTimes :NX_HEIGHT(&canvasRect) * yTimes];
		result = YES;
	}
	if(xTimes == 1)
		p.x = dlx + (NX_WIDTH(&bounds) - dlx - dux) / 2;
	else
		p.x = dlx + 3;
	if(yTimes == 1)
		p.y = dly + (NX_HEIGHT(&bounds) - dly - duy) / 2;
	else
		p.y = dly + 3;
	[tree setPositionTo:&p];
	return result;
}

- setAgent:aAgent
{
	agent = aAgent;
	return self;
}

- agent
{
	return agent;
}

- writePSToStream:(NXStream *)stream usingTree:aTree
{
    NXRect bbox;
	NXCoord dl, du;

    if(stream){
		pbConversion = YES;
		pbTree = aTree;
	    [aTree getTreeBounds:&bbox lowerWidth:&dl lowerHeight:&du];
	    [self copyPSCodeInside:&bbox to:stream];
		pbConversion = NO;
		pbTree = nil;
    }
    return self;
}

- (BOOL)zoomTo:(float)zoomX :(float)zoomY
{
	if(agent)
		[agent setDocScale:zoomX];
	return NO;
}

- (BOOL)acceptsFirstResponder
{
    return YES;
}

- (int)canvasX
{
	return xTimes;
}

- (int)canvasY
{
	return yTimes;
} 

- (BOOL)knowsPagesFirst:(int *)firstPageNum last:(int *)lastPageNum
{
	*lastPageNum = xTimes * yTimes;
	return YES;
}

- (BOOL)getRect:(NXRect *)theRect forPage:(int)page
{
	int row, col;
	
	if(page < 1 || page > xTimes * yTimes)
		return NO;
	page--;
	row = page / xTimes;
	col = page % xTimes;
	theRect->origin.x = col * NX_WIDTH(&canvasRect);
	theRect->origin.y = row * NX_HEIGHT(&canvasRect);
	theRect->size.width = NX_WIDTH(&canvasRect);
	theRect->size.height = NX_HEIGHT(&canvasRect);
	return YES;
}

- printPSCode:sender
{
	float printScaleFactor;
	
	printScaleFactor = [[agent printInfo] scalingFactor];
	if(printScaleFactor != 1)
		[self scale:printScaleFactor :printScaleFactor];
	[super printPSCode:sender];
	if(printScaleFactor != 1)
		[self scale: 1 / printScaleFactor :1 / printScaleFactor];
	return self;
}

@end


@implementation TreeView(PrivateMethods)

- updateCanvasPos:(NXCoord)xPos :(NXCoord)yPos
{
	canvasRect.origin.x = xPos;
	canvasRect.origin.y = yPos;
	if(!canvasPath){
		canvasPath = [[UPath allocFromZone:[self zone]] init];
		[canvasPath moveto:canvasRect.origin.x :canvasRect.origin.y];
		[canvasPath rlineto:canvasRect.size.width :0.0];
		[canvasPath rlineto:0.0 :canvasRect.size.height];
		[canvasPath rlineto:-canvasRect.size.width :0.0];
		[canvasPath rlineto:0.0 :-canvasRect.size.height];
	} else {
		canvasPath->bbox[0] = canvasRect.origin.x;
   		canvasPath->bbox[1] = canvasRect.origin.y;
    	canvasPath->bbox[2] = canvasRect.origin.x + canvasRect.size.width;
    	canvasPath->bbox[3] = canvasRect.origin.y + canvasRect.size.height;
		canvasPath->params[0] = canvasRect.origin.x;
		canvasPath->params[1] = canvasRect.origin.y;
		canvasPath->params[2] = canvasRect.size.width;
		canvasPath->params[3] = 0.0;
		canvasPath->params[4] = 0.0;
		canvasPath->params[5] = canvasRect.size.height;
		canvasPath->params[6] = -canvasRect.size.width;
		canvasPath->params[7] = 0.0;
		canvasPath->params[8] = 0.0;
		canvasPath->params[9] = -canvasRect.size.height;
	}
	return self;
}

@end

